﻿//------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace Microsoft.Samples.Kinect.SkeletonBasics
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Threading;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    using FileManager;
    using Microsoft.Kinect;
    using SketlonBackOffice;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        /// <summary>
        /// Width of output drawing
        /// </summary>
        private const float RenderWidth = 640.0f;

        /// <summary>
        /// Height of our output drawing
        /// </summary>
        private const float RenderHeight = 480.0f;

        /// <summary>
        /// Thickness of drawn joint lines
        /// </summary>
        private const double JointThickness = 3;

        /// <summary>
        /// Thickness of body center ellipse
        /// </summary>
        private const double BodyCenterThickness = 10;

        /// <summary>
        /// Thickness of clip edge rectangles
        /// </summary>
        private const double ClipBoundsThickness = 10;

        /// <summary>
        /// Brush used to draw skeleton center point
        /// </summary>
        private readonly Brush centerPointBrush = Brushes.Blue;

        /// <summary>
        /// Brush used for drawing joints that are currently tracked
        /// </summary>
        private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68));

        /// <summary>
        /// Brush used for drawing joints that are currently inferred
        /// </summary>        
        private readonly Brush inferredJointBrush = Brushes.Yellow;

        /// <summary>
        /// Pen used for drawing bones that are currently tracked
        /// </summary>
        private readonly Pen trackedBonePen = new Pen(Brushes.Green, 6);

        /// <summary>
        /// Pen used for drawing bones that are currently inferred
        /// </summary>        
        private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1);

        /// <summary>
        /// Active Kinect sensor
        /// </summary>
        private KinectSensor sensor;

        /// <summary>
        /// Drawing group for skeleton rendering output
        /// </summary>
        private DrawingGroup drawingGroup;

        /// <summary>
        /// Drawing image that we will display
        /// </summary>
        private DrawingImage imageSource;

        /// <summary>
        /// Bitmap that will hold color information
        /// </summary>
        private WriteableBitmap colorBitmap;

        /// <summary>
        /// Intermediate storage for the color data received from the camera
        /// </summary>
        private byte[] colorPixels;

        List<Line> JointLines = new List<Line>();
        List<SketlonFrame> SketlonList;
        bool IsVerified = false;
        List<double> degreeList = new List<double>();
        public MainWindow()
        {
            InitializeComponent();
            JointLines = new List<Line>();
            for (int i = 0; i < 10; i++)
            {
                JointLines.Add(new Line());
                DrawLineFirst(JointLines[i]);
            }
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
            degreeList.Add(0.0);
        }

        /// <summary>
        /// Draws indicators to show which edges are clipping skeleton data
        /// </summary>
        /// <param name="skeleton">skeleton to draw clipping information for</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        private static void RenderClippedEdges(Skeleton skeleton, DrawingContext drawingContext)
        {
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Bottom))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, RenderHeight - ClipBoundsThickness, RenderWidth, ClipBoundsThickness));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Top))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, RenderWidth, ClipBoundsThickness));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Left))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, ClipBoundsThickness, RenderHeight));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Right))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(RenderWidth - ClipBoundsThickness, 0, ClipBoundsThickness, RenderHeight));
            }
        }

        /// <summary>
        /// Execute startup tasks
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            // Create the drawing group we'll use for drawing
            this.drawingGroup = new DrawingGroup();

            // Create an image source that we can use in our image control
            this.imageSource = new DrawingImage(this.drawingGroup);

            // Display the drawing using our image control
            Image.Source = this.imageSource;

            // Look through all sensors and start the first connected one.
            // This requires that a Kinect is connected at the time of app startup.
            // To make your app robust against plug/unplug, 
            // it is recommended to use KinectSensorChooser provided in Microsoft.Kinect.Toolkit (See components in Toolkit Browser).
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (potentialSensor.Status == KinectStatus.Connected)
                {
                    this.sensor = potentialSensor;
                    break;
                }
            }

            if (null != this.sensor)
            {
                // Turn on the skeleton stream to receive skeleton frames
                this.sensor.SkeletonStream.Enable();

                // Add an event handler to be called whenever there is new color frame data
                this.sensor.SkeletonFrameReady += this.SensorSkeletonFrameReady;


                this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);

                // Allocate space to put the pixels we'll receive
                this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];

                // This is the bitmap we'll display on-screen
                this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth, this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);

                // Set the image we display to point to the bitmap where we'll put the image data
                this.VideoImage.Source = this.colorBitmap;

                // Add an event handler to be called whenever there is new color frame data
                this.sensor.ColorFrameReady += this.SensorColorFrameReady;

                // Start the sensor!
                try
                {
                    this.sensor.Start();
                }
                catch (IOException)
                {
                    this.sensor = null;
                }
            }
        }

        private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
            {
                if (colorFrame != null)
                {
                    // Copy the pixel data from the image to a temporary array
                    colorFrame.CopyPixelDataTo(this.colorPixels);

                    // Write the pixel data into our bitmap
                    this.colorBitmap.WritePixels(
                        new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
                        this.colorPixels,
                        this.colorBitmap.PixelWidth * sizeof(int),
                        0);
                }
            }
        }

        /// <summary>
        /// Execute shutdown tasks
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (null != this.sensor)
            {
                //FileManager.StreamRecorder.RecordStream(ToBeRecordedList);
                this.sensor.Stop();
            }
        }

        /// <summary>
        /// Event handler for Kinect sensor's SkeletonFrameReady event
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            Skeleton[] skeletons = new Skeleton[0];

            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame != null)
                {
                    skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    skeletonFrame.CopySkeletonDataTo(skeletons);
                }
            }

            using (DrawingContext dc = this.drawingGroup.Open())
            {
                // Draw a transparent background to set the render size
                dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight));

                if (skeletons.Length != 0)
                {
                    foreach (Skeleton skel in skeletons)
                    {
                        RenderClippedEdges(skel, dc);

                        if (skel.TrackingState == SkeletonTrackingState.Tracked)
                        {
                            this.DrawBonesAndJoints(skel, dc);
                        }
                        else if (skel.TrackingState == SkeletonTrackingState.PositionOnly)
                        {
                            dc.DrawEllipse(
                            this.centerPointBrush,
                            null,
                            this.SkeletonPointToScreen(skel.Position),
                            BodyCenterThickness,
                            BodyCenterThickness);
                        }
                    }
                }

                // prevent drawing outside of our render area
                this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight));
            }
        }

        /// <summary>
        /// Draws a skeleton's bones and joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        private void DrawBonesAndJoints(Skeleton skeleton, DrawingContext drawingContext)
        {
            // Render Torso
            this.DrawBone(skeleton, drawingContext, JointType.Head, JointType.ShoulderCenter);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderLeft);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderRight);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.Spine);
            this.DrawBone(skeleton, drawingContext, JointType.Spine, JointType.HipCenter);
            this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipLeft);
            this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipRight);

            // Left Arm
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderLeft, JointType.ElbowLeft);
            this.DrawBone(skeleton, drawingContext, JointType.ElbowLeft, JointType.WristLeft);
            this.DrawBone(skeleton, drawingContext, JointType.WristLeft, JointType.HandLeft);

            // Right Arm
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderRight, JointType.ElbowRight);
            this.DrawBone(skeleton, drawingContext, JointType.ElbowRight, JointType.WristRight);
            this.DrawBone(skeleton, drawingContext, JointType.WristRight, JointType.HandRight);

            // Left Leg
            this.DrawBone(skeleton, drawingContext, JointType.HipLeft, JointType.KneeLeft);
            this.DrawBone(skeleton, drawingContext, JointType.KneeLeft, JointType.AnkleLeft);
            this.DrawBone(skeleton, drawingContext, JointType.AnkleLeft, JointType.FootLeft);

            // Right Leg
            this.DrawBone(skeleton, drawingContext, JointType.HipRight, JointType.KneeRight);
            this.DrawBone(skeleton, drawingContext, JointType.KneeRight, JointType.AnkleRight);
            this.DrawBone(skeleton, drawingContext, JointType.AnkleRight, JointType.FootRight);

            // Render Joints
            foreach (Joint joint in skeleton.Joints)
            {
                Brush drawBrush = null;

                if (joint.TrackingState == JointTrackingState.Tracked)
                {
                    drawBrush = this.trackedJointBrush;
                }
                else if (joint.TrackingState == JointTrackingState.Inferred)
                {
                    drawBrush = this.inferredJointBrush;
                }

                if (drawBrush != null)
                {
                    drawingContext.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(joint.Position), JointThickness, JointThickness);
                }
            }
        }

        /// <summary>
        /// Maps a SkeletonPoint to lie within our render space and converts to Point
        /// </summary>
        /// <param name="skelpoint">point to map</param>
        /// <returns>mapped point</returns>
        private Point SkeletonPointToScreen(SkeletonPoint skelpoint)
        {
            // Convert point to depth space.  
            // We are not using depth directly, but we do want the points in our 640x480 output resolution.
            DepthImagePoint depthPoint = this.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30);
            return new Point(depthPoint.X, depthPoint.Y);
        }

        /// <summary>
        /// Draws a bone line between two joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw bones from</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        /// <param name="jointType0">joint to start drawing from</param>
        /// <param name="jointType1">joint to end drawing at</param>
        private void DrawBone(Skeleton skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1)
        {
            Joint joint0 = skeleton.Joints[jointType0];
            Joint joint1 = skeleton.Joints[jointType1];
            // If we can't find either of these joints, exit
            if (joint0.TrackingState == JointTrackingState.NotTracked ||
                joint1.TrackingState == JointTrackingState.NotTracked)
            {
                return;
            }

            // Don't draw if both points are inferred
            if (joint0.TrackingState == JointTrackingState.Inferred &&
                joint1.TrackingState == JointTrackingState.Inferred)
            {
                return;
            }

            // We assume all drawn bones are inferred unless BOTH joints are tracked
            Pen drawPen = this.inferredBonePen;
            if (joint0.TrackingState == JointTrackingState.Tracked && joint1.TrackingState == JointTrackingState.Tracked)
            {
                drawPen = this.trackedBonePen;
            }
            degreeList[0] = (LineHelpers.GetDegree(skeleton.Joints[JointType.Head].Position, skeleton.Joints[JointType.ShoulderCenter].Position));//Head bone degree
            degreeList[1] = (LineHelpers.GetDegree(skeleton.Joints[JointType.ShoulderCenter].Position, skeleton.Joints[JointType.ElbowLeft].Position));//Elbow left bone degree
            degreeList[2] = (LineHelpers.GetDegree(skeleton.Joints[JointType.ShoulderCenter].Position, skeleton.Joints[JointType.ElbowRight].Position));//Elbow right bone degree
            degreeList[3] = (LineHelpers.GetDegree(skeleton.Joints[JointType.ElbowLeft].Position, skeleton.Joints[JointType.HandLeft].Position));//hand left bone degree
            degreeList[4] = (LineHelpers.GetDegree(skeleton.Joints[JointType.ElbowRight].Position, skeleton.Joints[JointType.HandRight].Position));//hand right bone degree
            degreeList[5] = (LineHelpers.GetDegree(skeleton.Joints[JointType.ShoulderCenter].Position, skeleton.Joints[JointType.HipCenter].Position));//back bone degree
            degreeList[6] = (LineHelpers.GetDegree(skeleton.Joints[JointType.HipLeft].Position, skeleton.Joints[JointType.KneeLeft].Position));//knee left bone degree
            degreeList[7] = (LineHelpers.GetDegree(skeleton.Joints[JointType.HipRight].Position, skeleton.Joints[JointType.KneeRight].Position));//knee right bone degree
            degreeList[8] = (LineHelpers.GetDegree(skeleton.Joints[JointType.KneeLeft].Position, skeleton.Joints[JointType.FootLeft].Position));//foot left bone degree
            degreeList[9] = (LineHelpers.GetDegree(skeleton.Joints[JointType.KneeRight].Position, skeleton.Joints[JointType.FootRight].Position));//foot right bone degree

            drawingContext.DrawLine(drawPen, this.SkeletonPointToScreen(joint0.Position), this.SkeletonPointToScreen(joint1.Position));
        }
        private void DrawSketlon(SketlonFrame frame)
        {
            for (int i = 0; i < frame.Bones.Count; i++)
            {
                JointLines[i].SetValue(Line.X1Property, frame.Bones[i].X1);
                JointLines[i].SetValue(Line.X2Property, frame.Bones[i].X2);
                JointLines[i].SetValue(Line.Y1Property, frame.Bones[i].Y1);
                JointLines[i].SetValue(Line.Y2Property, frame.Bones[i].Y2);
            }
        }
        private void DrawXMLBones()
        {
            SketlonList = XMLReader.ReadXMLStream();
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            List<SketlonFrame> ToBeRepeatedList = new List<SketlonFrame>();
            worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                for (int i = 0; i < SketlonList.Count; i++)
                {
                    InvokeAndDraw(SketlonList[i]);
                    if (i < SketlonList.Count - 1 && SketlonList[i].RepeatCount > 0)
                    {
                        ToBeRepeatedList.Add(SketlonList[i]);
                        if (SketlonList[i].RepeatIndex != SketlonList[i + 1].RepeatIndex)
                        {
                            for (int j = 0; j < SketlonList[i].RepeatCount - 1; j++)
                            {
                                for (int k = 0; k < ToBeRepeatedList.Count; k++)
                                {
                                    InvokeAndDraw(ToBeRepeatedList[k]);
                                }
                            }
                            ToBeRepeatedList.Clear();
                        }
                    }
                    else if (i == SketlonList.Count - 1 && SketlonList[i].RepeatCount > 0)
                    {
                        ToBeRepeatedList.Add(SketlonList[i]);
                        for (int j = 0; j < SketlonList[i].RepeatCount - 1; j++)
                        {
                            for (int k = 0; k < ToBeRepeatedList.Count; k++)
                            {
                                InvokeAndDraw(ToBeRepeatedList[k]);
                            }
                        }
                        ToBeRepeatedList.Clear();
                    }

                }
            };
            worker.RunWorkerAsync();
        }

        private void InvokeAndDraw(SketlonFrame frame)
        {
            this.Dispatcher.Invoke((Action)(() =>
            {
                DrawSketlon(frame);
            }));
            Thread.Sleep(600);
            bool IsVerified = false;
            int counter = 0;
            for (int i = 0; i < frame.Bones.Count; i++)
            {
                if (frame.Bones[i].Degree < 0)
                    frame.Bones[i].Degree = frame.Bones[i].Degree * (-1);
            }
            for (int i = 0; i < degreeList.Count; i++)
            {
                if (degreeList[i] < 0)
                    degreeList[i] = degreeList[i] * (-1);
            }
            while (!IsVerified)
            {
                counter = 0;
                for (int i = 0; i < degreeList.Count; i++)
                {
                    if (frame.Bones[i].Degree < degreeList[i] + 10 && frame.Bones[i].Degree > degreeList[i] - 10)
                    {
                        counter++;
                    }
                    else
                        break;
                }
                if (counter == degreeList.Count && counter != 0)
                {
                    IsVerified = true;
                    ChangeContent("Başarılı");
                }
                else
                {
                    switch (counter)
                    {
                        case 0:
                            ChangeContent("Lütfen başınızı sarı çizgi ile eşleştirin.");
                            break;
                        case 1:
                            ChangeContent("Lütfen sol kolunuzu(dirsekten aşağı) sarı çizgi ile eşleştirin.");
                            break;
                        case 2:
                            ChangeContent("Lütfen sağ kolunuzu(dirsekten aşağı) sarı çizgi ile eşleştirin.");
                            break;
                        case 3:
                            ChangeContent("Lütfen sol dirseğinizi sarı çizgi ile eşleştirin.");
                            break;
                        case 4:
                            ChangeContent("Lütfen sağ dirseğinizi sarı çizgi ile eşleştirin.");
                            break;
                        case 5:
                            break;
                        case 6:
                            ChangeContent("Lütfen sol bacağınızı sarı çizgi ile eşleştirin.");
                            break;
                        case 7:
                            ChangeContent("Lütfen sağ bacağınızı sarı çizgi ile eşleştirin.");
                            break;
                        case 8:
                            ChangeContent("Lütfen sol dizinizi sarı çizgi ile eşleştirin.");
                            break;
                        case 9:
                            ChangeContent("Lütfen sağ dizinizi sarı çizgi ile eşleştirin.");
                            break;
                    }
                }
            }


        }
        private void ChangeContent(string content)
        {
            this.Dispatcher.Invoke((Action)(() =>
            {
                
                MessageLabel.Content = content;
            }));
        }
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            DrawXMLBones();
        }

        private void DrawLineFirst(Line line)
        {
            line.Stroke = System.Windows.Media.Brushes.Yellow;
            line.X1 = 0.0;
            line.X2 = 0.0;
            line.Y1 = 0.0;
            line.Y2 = 0.0;
            line.StrokeThickness = 6;
            WrapperCanvas.Children.Add(line);
        }

    }
}